﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace Tinter.Common.TypeMappers
{
    /// <summary>
    /// ITypeMapper implementation. 
    /// <remarks>
    /// Really, this implementation only register type map specs
    /// and create a global dictionary of mappers.
    /// </remarks>
    /// </summary>
    public sealed class TypeMapper : ITypeMapper
    {
        #region Properties

        private readonly Dictionary<string, ITypeMapSpec> specifications;

        /// <summary>
        /// Get the collection of ITypeMapSpec elements.
        /// </summary>
        public Dictionary<string, ITypeMapSpec> Specifications
        {
            get
            {
                return specifications;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Create a instance of TypeMapper
        /// </summary>
        public TypeMapper(IEnumerable<TypeMapSpecContainer> mapsModules)
        {
            //create map adapters dictionary
            specifications = new Dictionary<string, ITypeMapSpec>();

            if (mapsModules != null)
            {
                foreach (var module in mapsModules)
                {
                    foreach (var spec in module.Specifications)
                    {
                        specifications.Add(spec.Key, spec.Value);
                    }
                }
            }
        }

        #endregion

        #region ITypeMapper Implementation

        /// <summary>
        /// <see cref="Tinter.Common.TypeMappers.ITypeMapper"/>
        /// </summary>
        /// <typeparam name="TSource"><see cref="Tinter.Common.TypeMappers.ITypeMapper"/></typeparam>
        /// <typeparam name="TTarget"><see cref="Tinter.Common.TypeMappers.ITypeMapper"/></typeparam>
        /// <param name="source"><see cref="Tinter.Common.TypeMappers.ITypeMapper"/></param>
        /// <param name="moreSources"><see cref="Tinter.Common.TypeMappers.ITypeMapper"/></param>
        /// <returns><see cref="Tinter.Common.TypeMappers.ITypeMapper"/></returns>
        public TTarget Map<TSource, TTarget>(TSource source, params object[] moreSources)
            where TSource : class
            where TTarget : class
        {
            if (source == null)
            {
                throw new ArgumentNullException(MethodBase.GetCurrentMethod().GetParameters()[0].Name);
            }

            var descriptor = TypeMapSpecBase<TSource, TTarget>.GetDescriptor();
            TypeMapSpecBase<TSource, TTarget> spec;

            if (specifications.ContainsKey(descriptor))
            {
                spec = specifications[descriptor] as TypeMapSpecBase<TSource, TTarget>;

            }
            else // Uses default simple mapper.
            {
                spec = new TypeMapSpecBase<TSource, TTarget>();
                specifications[descriptor] = spec;
            }

            Debug.Assert(spec != null, "spec != null");
            return spec.Resolve(source, moreSources);
        }

        #endregion
    }
}
